package org.dyndns.opendemogroup.optimizer.problems;

import java.util.Random;

import org.dyndns.opendemogroup.optimizer.ArrayUtil;
import org.dyndns.opendemogroup.optimizer.ICustomCoding;
import org.dyndns.opendemogroup.optimizer.ICustomInitialization;
import org.dyndns.opendemogroup.optimizer.IOptimizationProblem;
import org.dyndns.opendemogroup.optimizer.Member;
import org.dyndns.opendemogroup.optimizer.Population;

public abstract class EvolutionStrategyBase implements IOptimizationProblem,
		ICustomCoding, ICustomInitialization
{

	protected final int memberSize;

	protected final int partSize;

	protected final int sigmaOffset;

	protected final int alphaOffset;

	protected double[] values;

	private final double[] paramMin;

	private final double[] paramMax;

	private final double[] sigmaMin;

	private final double[] sigmaMax;

	public EvolutionStrategyBase ( int memberSize )
	{
		this.partSize = memberSize;
		this.sigmaOffset = partSize;
		this.alphaOffset = partSize * 2;
		this.memberSize = 3 * this.partSize;
		values = new double[this.memberSize];

		paramMin = determineParameterMinimums ( );
		paramMax = determineParameterMaximums ( );
		sigmaMin = determineSigmaMinimums ( );
		sigmaMax = determineSigmaMaximums ( );
	}

	/**
	 * @see IOptimizationProblem#getGeneRange()
	 */
	public int getGeneRange ( )
	{
		assert false : "This method should not be called.";
		return 0;
	}

	/**
	 * @see IOptimizationProblem#getMemberSize()
	 */
	public int getMemberSize ( )
	{
		return memberSize;
	}

	/**
	 * @see IOptimizationProblem#isCombinatorialOptimization()
	 */
	public boolean isCombinatorialOptimization ( )
	{
		return false;
	}

	/**
	 * @see ICustomInitialization#initialize(Random, Population)
	 */
	public void initialize ( Random randomSource, Population population )
	{
		final int populationSize = population.getPopulationSize ( );
		for ( int i = 0; i < populationSize; i++ )
		{
			Member m = population.members[i];
			// { initialize parameter values
			for ( int j = 0; j < partSize; j++ )
			{
				m.genome[j] =
					( paramMax[j] - paramMin[j] ) * randomSource.nextDouble ( )
							+ paramMin[j];
			}
			// }

			// { initialize sigma values
			for ( int j = 0, s = sigmaOffset; j < partSize; j++, s++ )
			{
				m.genome[s] =
					( sigmaMax[j] - sigmaMin[j] ) * randomSource.nextDouble ( )
							+ sigmaMin[j];
			}
			// }

			// { initialize alpha values to 0.0
			for ( int j = 0, a = alphaOffset; j < partSize; j++, a++ )
			{
				m.genome[a] = 0.0;
			}
			// }
		}
	}

	public boolean makeValid ( )
	{
		boolean hasChanged = false;
		// { check parameter values
		for ( int i = 0; i < partSize; i++ )
		{
			if ( values[i] < paramMin[i] )
			{
				hasChanged = true;
				values[i] = paramMin[i];
			}
			else if ( values[i] > paramMax[i] )
			{
				hasChanged = true;
				values[i] = paramMax[i];
			}
		}
		// }

		return hasChanged;
	}

	/**
	 * @see ICustomCoding#decodeFrom(Member)
	 */
	public void decodeFrom ( Member source )
	{
		ArrayUtil.copy ( source.genome, values, 0, memberSize - 1 );
	}

	/**
	 * @see ICustomCoding#encodeTo(Member)
	 */
	public void encodeTo ( Member target )
	{
		ArrayUtil.copy ( values, target.genome, 0, memberSize - 1 );
	}

	protected abstract double[] determineParameterMinimums ( );

	protected abstract double[] determineParameterMaximums ( );

	protected abstract double[] determineSigmaMinimums ( );

	protected abstract double[] determineSigmaMaximums ( );

	public abstract boolean isMaximizing ( );

	public abstract double computeFitness ( Random randomSource );

}